<?php

namespace app\common\service\pay;

use app\common\service\ApiService;
use WeChatPay\Builder;
use WeChatPay\Crypto\AesGcm;
use WeChatPay\Crypto\Rsa;
use WeChatPay\Formatter;
use WeChatPay\Util\PemUtil;

class WxPayService extends ApiService
{

    private $appid;
    private $mchid;
    private $serial;
    private $ssl_key;
    private $pay_pem;
    private $v3_key;
    private $merchant_private_key_filePath;
    private $merchant_private_key_instance;
    private $platform_certificate_filePath;
    private $platform_public_key_instance;
    private $instance;

    public function __construct()
    {
        $this->appid = env('WXPAY_API_V3.WXPAY_APPID');
        $this->mchid = env('WXPAY_API_V3.WXPAY_MCHID');
        $this->serial = env('WXPAY_API_V3.WXPAY_SERIAL');
        $this->ssl_key = env('WXPAY_API_V3.WXPAY_SSL_KEY');
        $this->pay_pem = env('WXPAY_API_V3.WXPAY_PAY_PEM');
        $this->v3_key = env('WXPAY_API_V3.WXPAY_V3_KEY');
        if (!$this->appid || !$this->mchid || !$this->serial || !$this->ssl_key || !$this->pay_pem || !$this->v3_key) {
            getResponseException('请检查微信支付配置文件', [], 1, 'ERROR');
        }
        $root_path = app()->getRootPath();
        // 从本地文件中加载「商户API私钥」，「商户API私钥」会用来生成请求的签名
        $this->merchant_private_key_filePath = 'file://' . $root_path . $this->ssl_key;
        $this->merchant_private_key_instance = Rsa::from($this->merchant_private_key_filePath, Rsa::KEY_TYPE_PRIVATE);
        // 从本地文件中加载「微信支付平台证书」，用来验证微信支付应答的签名
        $this->platform_certificate_filePath = 'file://' . $root_path . $this->pay_pem;
        $this->platform_public_key_instance = Rsa::from($this->platform_certificate_filePath, Rsa::KEY_TYPE_PUBLIC);
        // 从「微信支付平台证书」中获取「证书序列号」
        $platform_certificate_serial = PemUtil::parseCertificateSerialNo($this->platform_certificate_filePath);
        $this->instance = Builder::factory([
            'mchid' => $this->mchid,
            'serial' => $this->serial,
            'privateKey' => $this->merchant_private_key_instance,
            'certs' => [
                $platform_certificate_serial => $this->platform_public_key_instance,
            ],
        ]);
    }

    //微信扫码支付
    public function nativePay($body, $attach, $orderNo, $amount, $notifyUrl, $endTime)
    {
        $notifyUrl = request()->domain() . "/index.php" . $notifyUrl;
        $json = [
            'appid' => $this->appid,
            'mchid' => $this->mchid,
            'description' => $body,
            'out_trade_no' => (string)$orderNo,
            'time_expire' => date('c', strtotime($endTime)),
            'notify_url' => $notifyUrl,
            'amount' => [
                'total' => (int)bcmul($amount, 100, 0),
                'currency' => 'CNY'
            ]
        ];
        if ($attach) {
            $json['attach'] = $attach;
        }
        // 发送请求
        try {
            $resp = $this->instance->chain('/v3/pay/transactions/native')->post(
                [
                    // 'debug' => true, // 调试模式
                    'json' => $json
                ]
            );
            $body = (string)$resp->getBody();
            $body = json_decode($body, true);
        } catch (\Exception $e) {
            // 进行异常捕获并进行错误判断处理
            if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
                $r = $e->getResponse();
                $body = (string)$r->getBody();
                $body = json_decode($body, true);
                logSave(['param' => $json, 'body' => $body], 'nativePay', 'wxPayService');
                getResponseException('支付错误,请检查异常', [], 1, 'ERROR');
            }
        }

        return $body['code_url'];
    }

    //公众号JSSDK 或 小程序 支付
    public function jsApiPay($openId, $body, $attach, $outTradeNo, $totalFee, $notifyUrl, $expireTime)
    {
        $notifyUrl = request()->domain() . "/index.php" . $notifyUrl;
        $json = [
            'appid' => $this->appid,
            'mchid' => $this->mchid,
            'description' => $body,
            'out_trade_no' => (string)$outTradeNo,
            'time_expire' => date('c', strtotime($expireTime)),
            'notify_url' => $notifyUrl,
            'amount' => [
                'total' => (int)bcmul($totalFee, 100, 0),
                'currency' => 'CNY'
            ],
            'payer' => [
                'openid' => $openId
            ]
        ];
        if ($attach) {
            $json['attach'] = $attach;
        }
        // 发送请求
        try {
            $resp = $this->instance->chain('/v3/pay/transactions/jsapi')->post(
                [
                    // 'debug' => true, // 调试模式
                    'json' => $json
                ]
            );
            $body = (string)$resp->getBody();
            $body = json_decode($body, true);
        } catch (\Exception $e) {
            // 进行异常捕获并进行错误判断处理
            if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
                $r = $e->getResponse();
                $body = (string)$r->getBody();
                $body = json_decode($body, true);
                logSave(['param' => $json, 'body' => $body], 'nativePay', 'wxPayService');
                getResponseException('支付错误,请检查异常', [], 1, 'ERROR');
            }
        }
        $params = [
            'appId' => $this->appid,
            'timeStamp' => (string)Formatter::timestamp(),
            'nonceStr' => Formatter::nonce(),
            'package' => 'prepay_id=' . $body['prepay_id'],
        ];
        $params['paySign'] = Rsa::sign(
            Formatter::joinedByLineFeed(...array_values($params)),
            $this->merchant_private_key_instance
        );
        $params['signType'] = 'RSA';
        return $params;
    }

    public function payNotify($function)
    {
        $request = app()->request;
        $inBody = file_get_contents('php://input');
        $header = $request->header();
        LogSave([
            'request' => $inBody,
            'header' => json_encode($header, true),
            'message' => '微信支付回调通知'
        ], 'payNotify', 'wxPayService');


        $inWechatpaySignature = $header['wechatpay-signature'];// 请根据实际情况获取
        $inWechatpayTimestamp = $header['wechatpay-timestamp'];// 请根据实际情况获取
        $inWechatpaySerial = $header['wechatpay-serial'];// 请根据实际情况获取
        $inWechatpayNonce = $header['wechatpay-nonce'];// 请根据实际情况获取


        $timeOffsetStatus = 300 >= abs(Formatter::timestamp() - (int)$inWechatpayTimestamp);
        $verifiedStatus = Rsa::verify(
            Formatter::joinedByLineFeed($inWechatpayTimestamp, $inWechatpayNonce, $inBody),
            $inWechatpaySignature,
            $this->platform_public_key_instance
        );
        if ($timeOffsetStatus && $verifiedStatus) {
            $inBodyArray = json_decode($inBody, true);
            [
                'resource' => [
                    'ciphertext' => $ciphertext,
                    'nonce' => $nonce,
                    'associated_data' => $aad
                ]] = $inBodyArray;
            $inBodyResource = AesGcm::decrypt($ciphertext, $this->v3_key, $nonce, $aad);
            // 把解密后的文本转换为PHP Array数组
            $inBodyResourceArray = (array)json_decode($inBodyResource, true);
            if ($inBodyResourceArray['trade_state'] != 'SUCCESS') {
                logSave([
                    'inBodyResourceArray' => $inBodyResourceArray,
                    'message' => '支付失败'
                ], '支付结果', 'wxPayService');
                return false;
            }
            return $function([
                'out_trade_no' => $inBodyResourceArray['out_trade_no'],
                'total_price' => bcdiv($inBodyResourceArray['amount']['total'], 100, 2),
                'transaction_id' => $inBodyResourceArray['transaction_id']
            ]);

        }
        return false;
    }

    public function ver()
    {

    }

}
